using System;
using System.ComponentModel;
using System.ComponentModel.Design;

using System.Drawing;
using System.Drawing.Drawing2D;

namespace Camalon.Drawing
{
	/// <summary>
	/// Intended to be used internally. 
	/// </summary>
    //Do not attach base designer..Causes 2005 ComponentTray to crash...!
    //[Designer("Camalon.Drawing.Design.ShapePenBaseDesigner, Camalon.Drawing.Design", typeof( IDesigner ))]
    [ToolboxItem(false)]
    public abstract class ShapePenBase : ContainerComponentBase	
	{
		int[]				_compounds		= new int[0];
		DashCap				_dashCap		= DashCap.Flat;
		float				_dashOffset		= 0f;
		int[]				_dashPattern	= new int[0];
		DashStyle			_dashStyle		= DashStyle.Solid;
		LineCapStyle		_endCap			= LineCapStyle.Default;
		LineJoin			_lineJoin		= LineJoin.Miter;
		float				_miterLimit		= 10f;
		LineCapStyle		_startCap		= LineCapStyle.Default;
		float				_width			= 1f;

		private LineCapStyle _lineCapStyle = LineCapStyle.Default;


		#region Constructor

		protected ShapePenBase()
		{
		}


		protected ShapePenBase(float width)
		{
			_width= width;
		}



		#endregion Constructor

		[Browsable( false )]
		public float DashOffsetGDI
		{
			get
			{
				return -_dashOffset / _width;
			}
		}


		public static float ValidateDashOffsetGDI( float dashPixelOffset, float penWidth )
		{
			if( penWidth == 0f) return 0f;
			return -dashPixelOffset/penWidth;

		}

		public float ValidateDashOffsetGDI( float penWidth )
		{
			
			if( penWidth == 0f) return 0f;
			return -_dashOffset/penWidth;

		}


		[Browsable( false )]
		public float[] DashPatternGDI
		{
			get
			{
				return ValidateDashPatternGDI( _dashPattern, _width );
			}

		}


		public static float[] ValidateDashPatternGDI( int[] pixelWidthValues, float penWidth )
		{
			if( pixelWidthValues == null || penWidth <=0 ) return null;

			//"Dash pattern values must consist of at least two positiv(non-zero) values";
			if(pixelWidthValues.Length == 0 || (pixelWidthValues.Length % 2) != 0 ) return null;
			foreach( int val in pixelWidthValues) if( val <=0 ) return null;

			float[] floats = new float[pixelWidthValues.Length];
			for (int i = 0; i < floats.Length; i++) floats[i] = ((float)pixelWidthValues[i])/penWidth;

			return floats;
		}


		public  float[] ValidateDashPatternGDI( float penWidth )
		{
			return ValidateDashPatternGDI( _dashPattern, penWidth );
		}


		#region ShouldSerialize

		[DesignOnly( true)]
		protected virtual bool ShouldSerializeCompounds()
		{
			return _compounds.Length > 0 ;
		}


		[DesignOnly( true)]
		protected virtual bool ShouldSerializeDashPattern()
		{
			return _dashPattern.Length > 0 ;
		}

		#endregion ShouldSerialize

		#region Properties


		[Description("An array of percent values that specify a compound pen. A compound pen draws a compound line made up of parallel lines and spaces.")]
		[RefreshProperties(RefreshProperties.Repaint)]
		public int[] Compounds
		{
			get{ return _compounds.Clone() as int[];}
			set
			{ 
				
				if( value == null ) _compounds	 = new int[0];
				else 
				{
					if( (value.Length % 2) != 0 ) 
					{
						string s = "Compound values must consist of values in pairs.";
						s+="Each pair describes the start and the end of the paralel lines relative to the pen's width";
						throw new InvalidOperationException(s);
					}
					
					//The elements in the array must be in increasing order, not less than 0, and not greater than 1.

					int lastVal = 0;
					for( int i= 0; i< value.Length; i++)
					{
						int val = value[i]; 
						if(val < lastVal || val <0 || val > 100  ) 
							throw new InvalidOperationException( "The elements in the array must be in increasing order, not less than 0%, and not greater than 100%");
						lastVal = val;
					}
					_compounds = value;
				}

				
				InvokeInternalChanged();
			}
		}


		[DefaultValue( DashCap.Flat)]
		[Description("Cap style used at the end of the dashes that make up dashed lines drawn with this ShapePen object.")]
		[RefreshProperties(RefreshProperties.Repaint)]
		[Category("Dash Style")]
		public DashCap DashCap
		{
			get{ return _dashCap;} 
			set
			{ 
				if( value == _dashCap) return;
				_dashCap = value;
				InvokeInternalChanged();
			}
		}
		
		
		[DefaultValue(0f)]
		[Description("The distance from the start of a line to the beginning of a dash pattern.")]
		[RefreshProperties(RefreshProperties.Repaint)]
		[Category("Dash Style")]
		public float DashOffset
		{
			get{ return _dashOffset;} 
			set
			{ 
				if( value == _dashOffset ) return;
				_dashOffset = value;
				InvokeInternalChanged();
			}
		}


		[Description("An array of real numbers that specify the lengths of alternating dashes and spaces in dashed lines.")]
		[RefreshProperties(RefreshProperties.All)]
		[Category("Dash Style")]
		public int[] DashPattern
		{
			get{ return _dashPattern.Clone() as int[];}
			set
			{ 
				if( value != null && value.Length > 0)
				{
					if( (value.Length % 2 ) != 0)
						throw new InvalidOperationException("Dash pattern values must consist of alternating dashes and spaces as a pair.");

					foreach( int val in value)
						if( val <=0) throw new InvalidOperationException("Dash pattern values must be positiv(non-zero)");
 
				}
							
				if( value == null ) _dashPattern	 = new int[0];
				else _dashPattern = value;

				InvokeInternalChanged();
			}
		}


		[DefaultValue( DashStyle.Solid)]
		[Description("The style used for dashed lines drawn with this ShapePen object.")]
		[RefreshProperties(RefreshProperties.Repaint)]
		[Category("Dash Style")]
        [Editor("Camalon.Drawing.Design.DashStyleEditor, Camalon.Drawing.Design", typeof(System.Drawing.Design.UITypeEditor))]
        public DashStyle DashStyle
		{
			get{ return _dashStyle;} 
			set
			{ 
				if( value == _dashStyle) return;
				_dashStyle = value;
				InvokeInternalChanged();
			}
		}


		[DefaultValue( LineJoin.Miter)]
		[Description(" The join style for the ends of two consecutive lines drawn with this ShapePen object.")]
		[RefreshProperties(RefreshProperties.Repaint)]
		public LineJoin LineJoin
		{
			get{ return _lineJoin;} 
			set
			{
				if( value == _lineJoin) return;
				_lineJoin = value;
				InvokeInternalChanged();
			}
		}


		[DefaultValue(10f)]
		[Description("The limit of the thickness of the join on a mitered corner.")]
		[RefreshProperties(RefreshProperties.Repaint)]
		public float MiterLimit
		{
			get{ return _miterLimit;} 
			set
			{ 
				if( value == _miterLimit ) return;
				_miterLimit = value;
				InvokeInternalChanged();
			}
		}


		[DefaultValue(typeof( LineCapStyle), "None")]
		[Description("The cap style used at the end of lines drawn with this ShapePen object.")]
		[Category("Cap Style")]
		public LineCapStyle EndCap
		{
			get{ return _endCap;}
			set
			{ 
				if( value == _endCap) return;
				_endCap = value;
				InvokeInternalChanged();
			}
		}


		[DefaultValue(typeof( LineCapStyle), "None")]
		[Description(" The cap style used at the beginning of lines drawn with this ShapePen object.")]
		[Category("Cap Style")]
		public LineCapStyle StartCap
		{
			get{ return _startCap;}
			set
			{ 
				if( value == _startCap) return;
				_startCap = value;
				InvokeInternalChanged();
			}
		}

			
		[DefaultValue(1.0f)]
		[Description("The width of this Pen object.")]
		[RefreshProperties(RefreshProperties.Repaint)]
		public float Width 
		{
			get{ return _width;} 
			set
			{ 
				if( value == _width ) return;
				if( value == 0) value = 1.0f;
				_width = value;
				InvokeInternalChanged();
			}
		}



		#endregion Properties

		#region Make Cap


		/// <summary>
		/// Offset to the End of the line
		/// </summary>
		/// <param name="capsize"></param>
		/// <param name="penWidth"></param>
		/// <param name="sizeFactor"></param>
		/// <returns></returns>
		private float CalculateCapOffset(CapSize capSize, float penWidth, float sizeFactor)
		{
			float offset = 0;
			float factor = 1.5f;
			switch( capSize )
			{
				case CapSize.SmallSmall:
					offset=penWidth;
					break;
				case CapSize.Small:
					offset=penWidth*factor;
					break;
				case CapSize.Normal:
					offset=penWidth*factor*factor;
					break;
				case CapSize.Large:
					offset=penWidth*factor*factor*factor;
					break;
				case CapSize.LargeLarge:
					offset=penWidth*factor*factor*factor*factor;
					break;
				case CapSize.VeryLarge:
					offset=penWidth*factor*factor*factor*factor*factor;
					break;
				default: break;

			}
			

			//return (float)System.Math.Round( 1f + (2f*offset/ penWidth));OLD
			offset = (float)System.Math.Round( 1f + (2f*offset/ penWidth));
			return offset * sizeFactor;

		}


		/*
		The coordinates for a line cap is based on a vertical line with an origin (0,0)
		at the end point of the line as shown below:

					   -Y
						+
						+
						+
						+
				  + + + + + + + 
				   +	*    +
					+   *   +
					 +	*  +
					  +	* +
					   + + 
		-X  * * * * * * + * * * * * *  X 
						*
						*
						*
						*
						*
					   +Y

		
	
		Note:A cap path to be filled using CustomLineCap, at least 1 line of path must 
		intersect the center in negative y-directon.
		*/


		private GraphicsPath MakeClosedCurlyBracket( LineCapStyle capStyle, float penWidth , out float baseInset  )
		{
			GraphicsPath path = new GraphicsPath();
			
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			bool empty = !capStyle.Filled;

	
			PointF[] ptsL	= new PointF[4];
			PointF[] ptsR	= new PointF[4];
			
			baseInset =capStyle.LineInset;

			if( capStyle.Align == CapAlign.Center )
			{
				float offsetCut = 0f;
				if( !empty ) 
				{
					offsetCut = (penWidth >=2  )? 0.05f:0.2f;
					if(baseInset <= 0 && baseInset > -(offset/6f)) baseInset = -offset/6f; 
				}

				ptsL[3] = new PointF( -offset,-offsetCut );
				ptsL[2] = new PointF( -offset,offset/2f  );
				ptsL[1] = new PointF( 0,-offsetCut );
				ptsL[0] = new PointF( 0,offset/2f);

				ptsR[3] = new PointF( 0,offset/2f);
				ptsR[2] = new PointF( 0,-offsetCut);
				ptsR[1] = new PointF( offset,offset/2f );
				ptsR[0] = new PointF( offset,-offsetCut);

			}
			else
			{
				
				if(baseInset >= 0 && baseInset < (offset/2f) ) baseInset = offset/2f;

				ptsL[3] = new PointF( -offset,-offset/2f );
				ptsL[2] = new PointF( -offset,0 );
				ptsL[1] = new PointF( 0,-offset/2f );
				ptsL[0] = new PointF( 0,0);

				ptsR[3] = new PointF( 0,0);
				ptsR[2] = new PointF( 0,-offset/2f );
				ptsR[1] = new PointF( offset,0f );
				ptsR[0] = new PointF( offset,-offset/2f );
				
			}

			path.AddBeziers(ptsL);
			path.AddBeziers(ptsR);
			if(empty) path.FillMode = FillMode.Winding;
			path.CloseAllFigures();
			return path;
		}


		private GraphicsPath MakeCurlyBracket( LineCapStyle capStyle, float penWidth , out float baseInset , bool closed )
		{
			if (closed) return MakeClosedCurlyBracket( capStyle, penWidth, out baseInset );

			GraphicsPath path = new GraphicsPath();
			
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			bool empty = !capStyle.Filled;

	
			PointF[] ptsL	= new PointF[4];
			PointF[] ptsR	= new PointF[4];
			
			baseInset =capStyle.LineInset;

			if( capStyle.Align == CapAlign.Center )
			{
				ptsL[0] = new PointF( -offset,0 );
				ptsL[1] = new PointF( -offset,offset/2f  );
				ptsL[2] = new PointF( 0,0 );
				ptsL[3] = new PointF( 0,offset/2f);

				ptsR[0] = new PointF( 0,offset/2f);
				ptsR[1] = new PointF( 0,0);
				ptsR[2] = new PointF( offset,offset/2f );
				ptsR[3] = new PointF( offset,0);

			}
			else
			{
				ptsL[0] = new PointF( -offset,-offset/2f );
				ptsL[1] = new PointF( -offset,0 );
				ptsL[2] = new PointF( 0,-offset/2f );
				ptsL[3] = new PointF( 0,0);

				ptsR[0] = new PointF( 0,0);
				ptsR[1] = new PointF( 0,-offset/2f );
				ptsR[2] = new PointF( offset,0f );
				ptsR[3] = new PointF( offset,-offset/2f );
				
			}

			path.AddBeziers(ptsL);
			path.AddBeziers(ptsR);
			path.FillMode = FillMode.Winding;
			return path;
		}



		private GraphicsPath MakeClosedCurlyBracketRTL( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			PointF[] ptsL	= new PointF[4];
			PointF[] ptsR	= new PointF[4];

			baseInset =capStyle.LineInset;
			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				float offsetCut = 0f;
				if( !empty ) 
				{
					offsetCut = (penWidth >=2  )? 0.1f:0.2f;
					if(baseInset <= 0 && baseInset > -(offset/4f)) baseInset = -offset/4f; 
				}

				ptsL[0] = new PointF( -offset,offset/2f );
				ptsL[1] = new PointF( -offset,0 );
				ptsL[2] = new PointF( 0f,offset/2f );
				ptsL[3] = new PointF( -offsetCut,-offsetCut);

				ptsR[0] = new PointF( offsetCut,-offsetCut);
				ptsR[1] = new PointF( 0f,offset/2f );
				ptsR[2] = new PointF( offset,0f);
				ptsR[3] = new PointF( offset,offset/2f );
			}
			else
			{

				float offsetOffset = empty ? offset/2f : offset/4f;
				if(baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset;
				
				float offsetCut = 0f;
				if( !empty ) offsetCut = (penWidth >=2  )? 0.1f:0.2f;

				ptsL[0] = new PointF( -offset,0f );
				ptsL[1] = new PointF( -offset,-offset/2f );
				ptsL[2] = new PointF( -offsetCut,0f );
				ptsL[3] = new PointF( -offsetCut,-offset/2f);

				ptsR[0] = new PointF( offsetCut,-offset/2f);
				ptsR[1] = new PointF( offsetCut,0f );
				ptsR[2] = new PointF( offset,-offset/2f );
				ptsR[3] = new PointF( offset,0f );
			}

			path.AddBeziers(ptsL);
			path.AddBeziers(ptsR);
			path.CloseAllFigures();
			if( empty ) path.FillMode = FillMode.Winding;

			return path;
		}


		private GraphicsPath MakeCurlyBracketRTL( LineCapStyle capStyle, float penWidth , out float baseInset, bool closed )
		{
			if( closed ) return MakeClosedCurlyBracketRTL(capStyle, penWidth, out baseInset );
			
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			PointF[] ptsL	= new PointF[4];
			PointF[] ptsR	= new PointF[4];

			baseInset =capStyle.LineInset;

			if( capStyle.Align == CapAlign.Center )
			{

				ptsL[0] = new PointF( -offset,offset/2f );
				ptsL[1] = new PointF( -offset,0 );
				ptsL[2] = new PointF( 0f,offset/2f );
				ptsL[3] = new PointF( 0f,0f);

				ptsR[0] = new PointF( 0f,0f);
				ptsR[1] = new PointF( 0f,offset/2f );
				ptsR[2] = new PointF( offset,0f);
				ptsR[3] = new PointF( offset,offset/2f );
			}
			else
			{

				if( baseInset >= 0 && baseInset < (offset/2f)) baseInset = offset/2f;

				ptsL[0] = new PointF( -offset,0f );
				ptsL[1] = new PointF( -offset,-offset/2f );
				ptsL[2] = new PointF( 0f,0f );
				ptsL[3] = new PointF( 0f,-offset/2f);

				ptsR[0] = new PointF( 0f,-offset/2f);
				ptsR[1] = new PointF( 0f,0f );
				ptsR[2] = new PointF( offset,-offset/2f );
				ptsR[3] = new PointF( offset,0f );
			}

			path.AddBeziers(ptsL);
			path.AddBeziers(ptsR);
			path.FillMode = FillMode.Winding;

			if( closed ) path.CloseAllFigures();
			return path;
		}


		private GraphicsPath MakeBracketRTL( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			PointF[] pts = new PointF[4];

			baseInset =capStyle.LineInset;

			if( capStyle.Align == CapAlign.Center )
			{
				pts[0] = new PointF( -offset,offset/4f );
				pts[1] = new PointF( -offset,0);
				pts[2] = new PointF( offset,0);
				pts[3] = new PointF( offset,offset/4f );
			}
			else
			{
				
				if( baseInset >= 0 && baseInset < (offset/4f) ) baseInset = offset/4f;
				pts[0] = new PointF( -offset,0f );
				pts[1] = new PointF( -offset,-offset/4f);
				pts[2] = new PointF( offset,-offset/4f);
				pts[3] = new PointF( offset,0f );
			}

			path.AddLines(pts);
			path.FillMode = FillMode.Winding;
			return path;
		}

		
		private GraphicsPath MakeBracket( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			PointF[] pts = new PointF[4];

			baseInset =capStyle.LineInset;

			if( capStyle.Align == CapAlign.Center )
			{

				pts[0] = new PointF( -offset,-offset/4f);
				pts[1] = new PointF( -offset,0f );
				pts[2] = new PointF( offset,0f );
				pts[3] = new PointF( offset,-offset/4f);

			}
			else
			{
				if( baseInset >= 0 && baseInset < (offset/4f) ) baseInset = offset/4f;

				pts[0] = new PointF( -offset,-offset/2f);
				pts[1] = new PointF( -offset,-offset/4f );
				pts[2] = new PointF( offset,-offset/4f );
				pts[3] = new PointF( offset,-offset/2f);


			}

			path.AddLines(pts);
			path.FillMode = FillMode.Winding;
			return path;
		}


		private GraphicsPath MakeMoonRTL( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			baseInset =capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			GraphicsPath path = new GraphicsPath();
			RectangleF rect;
			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Inset )
			{
				if( empty && baseInset >= 0 && baseInset < offset ) baseInset = offset; 
				else
				{
					float offsetOffset = (penWidth < 2.0f) ? 2f: 0.75f;
					if( baseInset >= 0 && baseInset < (offset*offsetOffset)) baseInset = offset*offsetOffset; 
				}
				rect = new RectangleF(-offset,-offset, 2f*offset, 2f*offset);
			}
			else
			{
				if(empty && baseInset >= 0 && baseInset < (offset/4)) baseInset = offset/4; 
				rect = new RectangleF(-offset,-offset/4f, 2f*offset, 2f*offset);
			}
			path.AddArc(rect,180f, 180);
			rect.Inflate( 0f, -offset/2f);
			path.AddArc(rect,0, -180f);
			if( empty )path.FillMode = FillMode.Winding;

			path.CloseAllFigures();
			return path;
		}


		private GraphicsPath MakeMoon( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			baseInset =capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			GraphicsPath path = new GraphicsPath();
			RectangleF rect;
			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Inset )
			{
				float offsetOffset = empty ? offset/2f : offset/4f;

				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				rect = new RectangleF(-offset,-2f*offset, 2f*offset, 2f*offset);
			}
			else
			{
				if(empty && baseInset >= 0 && baseInset < (offset/4f)) baseInset = offset/4f; 
				rect = new RectangleF(-offset,-1.75f*offset, 2f*offset, 2f*offset);
			}

			path.AddArc(rect,0f, 180f);
			rect.Inflate( 0f, -offset/2f);
			path.AddArc(rect,180f, -180f);

			if( empty )path.FillMode = FillMode.Winding;

			path.CloseAllFigures();
			return path;
		}


		private GraphicsPath MakeParenthesisRTL( LineCapStyle capStyle, float penWidth , out float baseInset)
		{
			baseInset =capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			GraphicsPath path = new GraphicsPath();
			RectangleF rect;

			bool empty = !capStyle.Filled;

			rect = new RectangleF(-offset,-offset, 2*offset, offset);
			float offsetCut = offsetCut = (penWidth >=2  )? 0.05f:0.2f;

			if( capStyle.Align == CapAlign.Inset )
			{
				if(baseInset >= 0 && baseInset < (offset/2f)) baseInset = offset/2f; 
				rect.Y+= offset/2f;
			}
			else
			{
				rect.Y-= offsetCut;
				rect.Y+= offset;

			}

			path.AddArc(rect,180f, 180);
			if( empty )path.FillMode = FillMode.Winding;
			if( !empty ) path.CloseAllFigures();
			return path;
		}


		private GraphicsPath MakeParenthesis( LineCapStyle capStyle, float penWidth , out float baseInset)
		{
			baseInset =capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			GraphicsPath path = new GraphicsPath();
			RectangleF rect;
			bool empty = !capStyle.Filled;
			

			rect = new RectangleF(-offset,-offset, 2*offset, offset);
			float offsetCut = offsetCut = (penWidth >=2  )? 0.05f:0.2f;

			if( capStyle.Align == CapAlign.Inset )
			{
				if( empty ) 
				{
					if(baseInset <= 0 && baseInset > -(offsetCut)) baseInset = offsetCut; 
					rect.Y-= offsetCut;
				}
				else if(baseInset >= 0 && baseInset < (offset/2f)) baseInset = offset/2f; 
			}
			else
			{
				rect.Y-= offsetCut;
				rect.Y+= offset/2f;

			}


			path.AddArc(rect,0, 180);
			if( empty )path.FillMode = FillMode.Winding;
			if( !empty ) path.CloseAllFigures();
			return path;
		}

		private GraphicsPath MakeHalfRoundRTL( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			baseInset =capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			GraphicsPath path = new GraphicsPath();
			RectangleF rect;

			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Inset )
			{
				float offsetOffset = empty ? offset : offset/2f;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				rect = new RectangleF(-offset,-offset, 2f*offset, 2f*offset);
			}
			else
			{
				if(empty && baseInset >= 0 && baseInset < (offset/2f)) baseInset = offset/2f; 
				rect = new RectangleF(-offset,-offset/2f, 2f*offset, 2f*offset);
			}

			path.AddArc(rect,180f, 180);
			if( empty )path.FillMode = FillMode.Winding;

			path.CloseAllFigures();
			return path;
		}

		private GraphicsPath MakeHalfRound( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			baseInset =capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			GraphicsPath path = new GraphicsPath();
			RectangleF rect;
			bool empty = !capStyle.Filled;
			
			if( capStyle.Align == CapAlign.Inset )
			{
				float offsetOffset = empty ? offset : offset/2f;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				rect = new RectangleF(-offset,-2f*offset, 2f*offset, 2f*offset);
			}
			else
			{
				if(empty && baseInset >= 0 && baseInset < (offset/2f)) baseInset = offset/2f; 
				rect = new RectangleF(-offset,-1.5f*offset, 2f*offset, 2f*offset);
			}

			path.AddArc(rect,0f, 180);
			if( empty )path.FillMode = FillMode.Winding;
			path.CloseAllFigures();
			return path;
		}

		private GraphicsPath MakeRound( LineCapStyle capStyle, float penWidth , out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			baseInset = capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			RectangleF rect;

			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				if(empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 
				rect = new RectangleF(-offset,-offset, 2*offset, 2*offset);
			}
			else
			{
				float offsetOffset = empty ? 2f*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				rect = new RectangleF(-offset,-2f*offset, 2f*offset, 2f*offset);
			}

			path.AddEllipse(rect);
			if( empty )path.FillMode = FillMode.Winding;
			return path;
		}


		private GraphicsPath MakeSquare( LineCapStyle capStyle, float penWidth, out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			baseInset = capStyle.LineInset;
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			RectangleF rect;

			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				if(empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 
				rect = new RectangleF(-offset,-offset, 2*offset, 2*offset);
			}
			else
			{
				float offsetOffset = empty ? 2f*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				rect = new RectangleF(-offset,-2f*offset, 2f*offset, 2f*offset);
			}

			path.AddRectangle(rect);
			if( empty )path.FillMode = FillMode.Winding;

			return path;
		}


		private GraphicsPath MakeDiamond( LineCapStyle capStyle, float penWidth, out float baseInset  )
		{
			baseInset = capStyle.LineInset;

			GraphicsPath path = new GraphicsPath();

			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			bool empty = !capStyle.Filled;

			PointF[] pts = new PointF[4];
			if( capStyle.Align == CapAlign.Center )
			{
				if(empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 
				pts[0] = new PointF( offset,0 );
				pts[1] = new PointF( 0,offset );
				pts[2] = new PointF( -offset,0 );
				pts[3] = new PointF( 0,-offset );
			}
			else
			{
				float offsetOffset = empty ? 2f*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				pts[0] = new PointF( 0,0 );
				pts[1] = new PointF( -offset,-offset );
				pts[2] = new PointF( 0,-2f*offset );
				pts[3] = new PointF( offset,-offset );
			}

			//path.AddLines(pts);
			path.AddPolygon(pts);
			if( empty )path.FillMode = FillMode.Winding;

			path.CloseAllFigures();

			return path;
		}


		private GraphicsPath MakeArrowRTL( LineCapStyle capStyle, float penWidth, out float baseInset  )
		{

			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[4];

			baseInset =capStyle.LineInset;
			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				if(empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 

				pts[0] = new PointF( -offset,offset );
				pts[1] = new PointF( 0f,-offset);
				pts[2] = new PointF( offset,offset );
				pts[3] = new PointF( 0f,0f );
			}
			else
			{
				float offsetOffset = empty ? 2f*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 

				pts[0] = new PointF( -offset,0 );
				pts[1] = new PointF( 0f,-2f*offset);
				pts[2] = new PointF( offset,0 );
				pts[3] = new PointF( 0f,-offset );
			}

			path.AddPolygon(pts);
			if( empty )path.FillMode = FillMode.Winding;

			return path;
		}


		private GraphicsPath MakeArrow( LineCapStyle capStyle, float penWidth, out float baseInset  )
		{

			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[4];

			baseInset =capStyle.LineInset;
			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				pts[0] = new PointF( offset,-offset );
				pts[1] = new PointF( 0,offset);
				pts[2] = new PointF( -offset,-offset );
				//Path to be filled: At least 1 line must overcut the center in negative y-directon
				float offsetCut = 0f;
				if( !empty) offsetCut = (penWidth < 2 )? -0.2f:-0.1f;
				pts[3] = new PointF( 0,offsetCut );

			}
			else
			{
				if(empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 
				else
				if( baseInset >= 0 && baseInset < (offset/2f)) baseInset = offset/2f; 
				//CAUTION:Here order of the points is very importent.
				pts[0] = new PointF( 0f,0f );
				pts[1] = new PointF( -offset,-2f*offset);
				pts[2] = new PointF( 0f,-offset );
				pts[3] = new PointF( offset,-2f*offset );
			}


			path.AddPolygon(pts);

			if( empty )path.FillMode = FillMode.Winding;

			return path;
		}

		
		private GraphicsPath MakePotRTL( LineCapStyle capStyle, float penWidth, out float baseInset )
		{
			baseInset =capStyle.LineInset;
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[4];

			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Inset)
			{
				float offsetOffset = empty ? 2f*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 

				pts[0] = new PointF( -offset, 0f);
				pts[1] = new PointF( -offset/2f,-2f*offset );
				pts[2] = new PointF( offset/2f,-2f*offset );
				pts[3] = new PointF( offset, 0f);
			}
			else
			{
				if(empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 

				pts[0] = new PointF( -offset, offset);
				pts[1] = new PointF( -offset/2f,-offset );
				pts[2] = new PointF( offset/2f,-offset );
				pts[3] = new PointF( offset, offset);
			}

			path.AddPolygon(pts);
			if( empty )path.FillMode = FillMode.Winding;

			return path;
		}


		private GraphicsPath MakePot( LineCapStyle capStyle, float penWidth, out float baseInset )
		{
			baseInset =capStyle.LineInset;
			GraphicsPath path = new GraphicsPath();

			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);

			bool empty = !capStyle.Filled;

			PointF[] pts = new PointF[4];

			if( capStyle.Align == CapAlign.Inset)
			{
				float offsetOffset = empty ? 2f*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 

				pts[0] = new PointF( -offset/2f, 0f);
				pts[1] = new PointF( -offset,-2f*offset );
				pts[2] = new PointF( offset,-2f*offset );
				pts[3] = new PointF( offset/2f, 0f);
			}
			else
			{
				if(empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 

				pts[0] = new PointF( -offset/2f, offset);
				pts[1] = new PointF( -offset,-offset );
				pts[2] = new PointF( offset,-offset );
				pts[3] = new PointF( offset/2f, offset);
			}

			path.AddPolygon(pts);
			if( empty )path.FillMode = FillMode.Winding;

			return path;
		}

		
		private GraphicsPath MakeTriangle( LineCapStyle capStyle, float penWidth, out float baseInset )
		{

			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[3];
			baseInset =capStyle.LineInset;

			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				if( empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 
				pts[0] = new PointF( offset,-offset );
				pts[1] = new PointF( 0,offset);
				pts[2] = new PointF( -offset,-offset );
			}
			else
			{
				//CAUTION:Here order of the points is very importent.
				float offsetOffset = empty ? 2*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 

				pts[0] = new PointF( 0f,0f );
				pts[1] = new PointF( -offset,-2f*offset);
				pts[2] = new PointF( offset,-2f*offset );
			}

			path.AddPolygon(pts);
			if( empty )path.FillMode = FillMode.Winding;

			return path;
		}

		private GraphicsPath MakeArrowLine( LineCapStyle capStyle, float penWidth, out float baseInset  )
		{

			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[3];
			baseInset =capStyle.LineInset;

			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				//if( empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 
				pts[0] = new PointF( offset,-offset );
				pts[1] = new PointF( 0,offset);
				pts[2] = new PointF( -offset,-offset );
			}
			else
			{
				pts[0] = new PointF( -offset,-2f*offset);
				pts[1] = new PointF( 0f,0f );
				pts[2] = new PointF( offset,-2f*offset );
			}

			path.AddLines(pts);
			path.FillMode = FillMode.Winding;

			return path;
		}
		
		private GraphicsPath MakeTriangleRTL( LineCapStyle capStyle, float penWidth, out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[3];
			baseInset =capStyle.LineInset;

			bool empty = !capStyle.Filled;

			if( capStyle.Align == CapAlign.Center )
			{
				if( empty && baseInset >= 0 && baseInset < offset) baseInset = offset; 

				pts[0] = new PointF( -offset,offset );
				pts[1] = new PointF( 0,-offset);
				pts[2] = new PointF( offset,offset );
			}
			else
			{
				//CAUTION:Here order of the points is very importent.
				float offsetOffset = empty ? 2*offset : offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				pts[0] = new PointF( -offset,0f );
				pts[1] = new PointF( 0,-2f*offset);
				pts[2] = new PointF( offset,0f );
			}
			
			path.AddPolygon(pts);
			if( empty )path.FillMode = FillMode.Winding;

			return path;
		}


		private GraphicsPath MakeArrowLineRTL( LineCapStyle capStyle, float penWidth, out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[3];
			baseInset =capStyle.LineInset;

			if( capStyle.Align == CapAlign.Center )
			{
				pts[0] = new PointF( -offset,2*offset );
				pts[1] = new PointF( 0,0);
				pts[2] = new PointF( offset,2*offset );
			}
			else
			{
				//CAUTION:Here order of the points is very importent.
				float offsetOffset = 2*offset;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				pts[0] = new PointF( -offset,0f );
				pts[1] = new PointF( 0,-2f*offset);
				pts[2] = new PointF( offset,0f );
			}
			
			path.AddLines(pts);
			path.FillMode = FillMode.Winding;

			return path;
		}


		private GraphicsPath MakeLine( LineCapStyle capStyle, float penWidth, out float baseInset )
		{
			GraphicsPath path = new GraphicsPath();
			float offset = CalculateCapOffset( capStyle.Size, penWidth, capStyle.SizeFactor);
			PointF[] pts = new PointF[2];
			baseInset =capStyle.LineInset;

			if( capStyle.Align == CapAlign.Center )
			{
				pts[0] = new PointF( -offset,0 );
				pts[1] = new PointF( offset, 0);
			}
			else
			{
				float offsetOffset = offset/2f;
				if( baseInset >= 0 && baseInset < offsetOffset) baseInset = offsetOffset; 
				pts[0] = new PointF( -offset,-offsetOffset );
				pts[1] = new PointF( offset,-offsetOffset);
			}
			
			path.AddLines(pts);
			path.FillMode = FillMode.Winding;
			return path;
		}



		public CustomLineCap MakeCap( LineCapStyle capStyle, float penWidth /* Matrix transform */)
		{
			if( capStyle.Style == CapStyle.None) return null;

			
			float baseInset = 0f;
			GraphicsPath path = null;
			switch( capStyle.Style )
			{
				case CapStyle.Line				: path = MakeLine( capStyle, penWidth, out baseInset);break;
				case CapStyle.Triangle			: path = MakeTriangle( capStyle, penWidth, out baseInset);break;
				case CapStyle.TriangleRTL		: path = MakeTriangleRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.Arrow				: path = MakeArrow( capStyle, penWidth, out baseInset);break;
				case CapStyle.ArrowRTL			: path = MakeArrowRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.ArrowLine			: path = MakeArrowLine( capStyle, penWidth, out baseInset);break;
				case CapStyle.ArrowLineRTL		: path = MakeArrowLineRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.Trapezoid				: path = MakePot( capStyle, penWidth, out baseInset);break;
				case CapStyle.TrapezoidRTL			: path = MakePotRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.Diamond			: path = MakeDiamond( capStyle, penWidth, out baseInset);break;
				case CapStyle.Round				: path = MakeRound( capStyle, penWidth, out baseInset);break;
				case CapStyle.Square			: path = MakeSquare( capStyle, penWidth, out baseInset);break;
				case CapStyle.Semiround			: path = MakeHalfRound( capStyle, penWidth, out baseInset);break;
				case CapStyle.SemiroundRTL		: path = MakeHalfRoundRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.Parenthesis		: path = MakeParenthesis( capStyle, penWidth, out baseInset);break;
				case CapStyle.ParenthesisRTL	: path = MakeParenthesisRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.Moon				: path = MakeMoon( capStyle, penWidth, out baseInset);break;
				case CapStyle.MoonRTL			: path = MakeMoonRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.Bracket			: path = MakeBracket( capStyle, penWidth, out baseInset);break;
				case CapStyle.BracketRTL		: path = MakeBracketRTL( capStyle, penWidth, out baseInset);break;
				case CapStyle.CurlyBracket		: path = MakeCurlyBracket( capStyle, penWidth, out baseInset, false);break;
				case CapStyle.CurlyBracketRTL	: path = MakeCurlyBracketRTL( capStyle, penWidth, out baseInset, false);break;
				case CapStyle.ClosedCurlyBracket		: path = MakeCurlyBracket( capStyle, penWidth, out baseInset, true);break;
				case CapStyle.ClosedCurlyBracketRTL		: path = MakeCurlyBracketRTL( capStyle, penWidth, out baseInset, true);break;


				default: throw new InvalidEnumArgumentException( "CapStyle",(int) capStyle.Style, typeof( CapStyle));
			}			

			#region Transformation doesn't work..... 
//			if( transform != null && !transform.IsIdentity)
//			{
//				PointF p1 = RectangleUtil.CenterPoint( path.GetBounds( ) );
//				PointF p2 = RectangleUtil.CenterPoint( path.GetBounds( transform ) );
//				float dx = p1.X - p2.X;
//				float dy = p1.Y - p2.Y;
//				using (Matrix m = transform.Clone() as Matrix )
//				{
//					m.Translate( dx, dy, MatrixOrder.Append);
//					path.Transform( m );
//				}
//			}

			#endregion



			bool fill = path.FillMode == FillMode.Alternate; 
			
			CustomLineCap lineCap;

			if( fill ) lineCap = new CustomLineCap( path, null);
			else lineCap = new CustomLineCap( null, path);

			lineCap.BaseInset= baseInset;

			return lineCap;
		}

		
		public void MakePenCap( ShapePenBase shapePen, Pen pen)
		{
			if( pen == null || shapePen == null) return;

			LineCap dashCap = (LineCap)Enum.Parse(typeof(LineCap),  Enum.GetName( typeof( DashCap ), shapePen.DashCap ));
			
			pen.StartCap	= LineCap.Flat;
			pen.EndCap		= LineCap.Flat;

			LineCapStyle startCap = shapePen.StartCap;

			if( startCap.Style != CapStyle.None )
			{
				if( startCap.Style == CapStyle.RoundFitWidth) pen.StartCap = LineCap.Round;
				else if( startCap.Style == CapStyle.TriangleFitWidth ) pen.StartCap = LineCap.Triangle;
				else
				{
					pen.StartCap = LineCap.Custom;
					CustomLineCap lineCap = MakeCap( startCap, shapePen.Width );
					lineCap.StrokeJoin = shapePen.LineJoin;
					lineCap.SetStrokeCaps( dashCap, dashCap );

					pen.CustomStartCap = lineCap;
				}
			}

			LineCapStyle endCap = shapePen.EndCap;
			

			if( endCap.Style != CapStyle.None )
			{
				if( endCap.Style == CapStyle.RoundFitWidth) pen.EndCap = LineCap.Round;
				else if( endCap.Style == CapStyle.TriangleFitWidth ) pen.EndCap = LineCap.Triangle;
				else
				{
					pen.EndCap = LineCap.Custom;
					CustomLineCap lineCap = MakeCap( endCap, shapePen.Width );

					lineCap.StrokeJoin = shapePen.LineJoin;
					lineCap.SetStrokeCaps( dashCap, dashCap );
					pen.CustomEndCap = lineCap;
				}
			}


		}



		public void MakeCap( Pen pen )
		{
			MakePenCap( this, pen);
		}


		#endregion Make Cap Style

	}
}
